<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
  <title>ChatGPT</title>

  <link rel="shortcut icon" href="icon.png" />

  <!-- <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.2/axios.min.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.47/vue.global.prod.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdn.bootcdn.net/ajax/libs/element-plus/2.2.30/index.css">
  <script src="https://cdn.bootcdn.net/ajax/libs/element-plus/2.2.30/index.full.min.js"></script> -->
  <script src="/cdn_libs/vue.global.prod.js"></script>
  <link href="/cdn_libs/index.css" rel="stylesheet" type="text/css" />


  <script src="/cdn_libs/axios.min.js"></script>
  <script src="/cdn_libs/index.full.js"></script>

  <style type="text/css">
    html,body,#app{
      height: 100%;
      margin:0;
    }

    .chat{
      background-color: #272a37;
      height: 100%;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
    }
    .chat .head{
      text-align: center;
      padding-left: 20px;
      padding-right: 20px;
      color: #fff;
    }

    .chat .body{
      background-color: #323644;
      overflow: auto;
      height:100%; 
    }
    .chat .body::-webkit-scrollbar {
      width: 5px;
    }
    .chat .body::-webkit-scrollbar-track {
      background: #e0e0e0;
      border-radius: 3px;
    }
    .chat .body::-webkit-scrollbar-thumb {
      background-color: #c2c2c2;
      border-radius: 3px;
    }
    .chat-msg{
      display: flex;
      padding:20px 10px 10px 10px;
    }
    .chat .body .assistant{
      justify-content:flex-start;
    }
    .chat .body .assistant .content{
      background-color: #d9d9d9;
    }
    .chat .body .user{
      justify-content:flex-end;
    }
    .chat .body .user .content{
      background-color: #1d90f5;
      color:#fff;
    }
    .chat-msg .content{
      white-space: pre-wrap;
      font-size: 14px;
      max-width: 80%;
      margin:0 10px;
      padding:10px;
      border-radius: 8px;
    }
    .el-loading-mask{
      border-radius: 8px;
    }

    .chat .footer{
      padding: 20px;
      display: flex;
      justify-content: space-between;
    }
  </style>
</head>

<body>
<div id="app">
  <div class="chat">
    <div class="head">
      <!-- ApiKey<el-input v-model="apikey" type="password" style="width: 50%;margin:0 10px;" placeholder="请输入ChatGPT的API密钥"></el-input>
      <el-button type="primary" @click="saveKey">保存到浏览器</el-button>
      <el-button type="primary" @click="delKey">删除保存的密钥</el-button> -->
      <h4 style="height: 5%;">角色设定</h4>
      <el-input v-model="system" type="textarea" style="width: 100%;" placeholder="请输入ChatGPT的角色设定(超过2000字可能出错不返回)" rows="2"></el-input>
    </div>
    <div id="chat-body" class="body">
      <div v-for="v in msg" class="chat-msg" :class="v.role">
        <el-avatar v-if="v.role=='assistant'" :size="30" src="icon.png"></el-avatar>
        <img v-if="v.content.startsWith('data:image/png;base64')" style="width:50%;" :src=v.content class="ai-image" alt="generated image"/>
        <pre v-else class="content">{{v.content}}</pre>
      </div>
      <!-- 思考中图标 -->
      <div v-show="!canSend" class="chat-msg assistant">
        <el-avatar :size="30" src="icon.png"></el-avatar>
        <pre class="content animate__animated animate__fadeInUp">{{tmpTxt}}</pre>
      </div>
    </div>
    <el-select v-model="model" style="padding-left: 20px;padding-right: 20px;" class="m-2" placeholder="Model" size="large">
      <el-option v-for="item in models" :key="item.id" :label="item.name" :value="item.id">{{
          item.name
        }}</el-option>
    </el-select>
    <div class="footer">
      <el-input v-model="input" :disabled="!canSend" type="textarea" placeholder="请输入内容(超过2000字可能出错不返回)" @keypress="pressEnter" rows="5" ></el-input>
      <div style="height: 100%;">
        <el-button type="primary" :disabled="!canSend" style="height: 45%;margin-left: 38%;" @click="send">发送</el-button>
        <el-button type="primary" style="height: 45%;margin-left: 38%;margin-top: 10%;" @click="downloadTxt">保存</el-button>
      </div>
    </div>
  </div>
</div>

<script type="text/javascript">
  var app=Vue.createApp({
    data(){
      return{
        // saveName:'chatgpt-apikey',
        // apikey:'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',
        msg:[],//所有对话
        messages:[],//所有对话
        input:'',//输入
        canSend:true,
        tmpTxt:'',//临时流数据
        model: 'OpenAi00',
        models: [],
        system: '你是一个乐于助人的AI助手',
        systemFlag: false,
        uid: null,
      }
    },
    computed:{

    },
    mounted() {
      this.getModels();
    },
    created(){
      // this.apikey=localStorage.getItem(this.saveName)
    },
      watch: {
        model(model) {
          if (model !== 'OpenAi51'){
            this.systemFlag=false;
          }
        },
        system(system) {
          if (system == '') {
            this.systemFlag = true;
          } else {
            this.systemFlag = false;
            this.messages=[];
          }
        }
      },
    methods:{
        getModels() {
           axios.post('https://liaocat.msns.cn:1443/api/models', {}, {
            headers: {
              'Content-Type': 'application/json',
              // 'Authorization': `Bearer ` + this.key,
            }
          })
            .then(response => {
              this.models = response.data.models;
              console.log(this.models)
            })
            .catch(error => {
              console.log(error);
            });
        },
      downloadTxt() {
        let element = document.createElement("a");
        let text ='';
        this.messages.forEach(element => {
          if(element.role=='user'){
            text+='\r\n你：'+element.content+'\r\n';
          }else if(element.role=='assistant'){
            text+='\r\nAI：'+element.content+'\r\n';
          }else if(element.role=='system'){
            text+='\r\n设定：'+element.content+'\r\n';
          }
        });
        let file = new Blob([text], { type: 'text/plain' });
        element.href = URL.createObjectURL(file);
        element.download = "download.txt";
        element.click();
      },
      //回车
      pressEnter(){
        if(event.keyCode == 13){
          event.preventDefault()//禁止默认换行
          this.send()
        }
      },
      //发送消息
      send(){
        //判断apikey
        // if(!this.apikey){
        //   this.$message.error('请输入ChatGPT的API密钥')
        //   return
        // }
        //判断发言
        if(!this.input){
          this.$message.error('请输入问题')
          return
        }
        if(!this.systemFlag){
          this.messages.push({
              role:"system",
              content: this.system});
              this.systemFlag = true;
          }
          if(this.messages.length>11){
            this.messages.splice(1,this.messages.length-11)
          }
        //显示你的发言
        this.msg.push({role:'user',content:this.input})
        this.messages.push({role:'user',content:this.input})

        //禁止输入和发送
        this.canSend=false

        //滚动lhtask
        let chatBody=document.getElementById('chat-body')
        setTimeout(()=>{chatBody.scrollTop = chatBody.scrollHeight},100)

        let data = {
            messages: this.messages,
            model: this.model
          }
        let ainFlag = true
        let waitFlag = true
        let ain = setInterval(() => {
               if(waitFlag){
                if(ainFlag){
                  this.tmpTxt = this.tmpTxt.replace('______','')
                  this.tmpTxt+="____"
                  ainFlag = false
                }else{
                  this.tmpTxt = this.tmpTxt.replace('____','')
                  this.tmpTxt+="______"
                  ainFlag = true
                }
              }
        }, 100);
        if (this.model == 'OpenAi50') {
            data = {
              prompt: this.input,
              model: this.model
            }
          }
        //清空输入框
        this.input=''

        axios.post('https://liaocat.msns.cn:1443/api/completions', data, {
            headers: {
              'Content-Type': 'application/json'
            }
          }).then(response => {
              if (this.model == 'OpenAi50') {
                this.canSend=true
                waitFlag = false
                clearInterval(ain)
                this.tmpTxt = this.tmpTxt.replace('______','')
                this.tmpTxt = this.tmpTxt.replace('____','')
                this.msg.push({role:'assistant',content:response.data.choices});
                //滚动到底部
                chatBody.scrollTop = chatBody.scrollHeight
              }else{
                this.uid = response.data.uid;
                if (window.EventSource) {
                  ainFlag = true
                  waitFlag = true
                  const source = new EventSource('https://liaocat.msns.cn:1443/api/sse?uid='+this.uid);
                  let reconnectTimeout;
  
                  source.addEventListener('open', () => {
                    console.log('Connected');
                    clearTimeout(reconnectTimeout);
                  }, false);
                  
                  source.addEventListener('close', e => {
                  this.msg.push({role:'assistant',content:this.tmpTxt})
                  this.messages.push({role:'assistant',content:this.tmpTxt})
                    source.close();
                    waitFlag = false
                    clearInterval(ain)
                    this.tmpTxt = this.tmpTxt.replace('______','')
                    this.tmpTxt = this.tmpTxt.replace('____','')
                    this.canSend=true
                    this.tmpTxt=''
                    //滚动到底部
                    chatBody.scrollTop = chatBody.scrollHeight
                  }, false);
  
                  source.addEventListener('pause', e => {
                    source.close();
                    const reconnectTime = +e.data;
                    const currentTime = +new Date();
                    reconnectTimeout = setTimeout(() => {
                    }, reconnectTime - currentTime);
                  }, false);
  
                  // 服务器发送信息到客户端时，如果没有 event 字段，默认会触发 message 事件
                  source.addEventListener('message', e => {
                    waitFlag = false
                    console.log(`data: ${e.data}`);
                    this.tmpTxt = this.tmpTxt.replace('______','')
                    this.tmpTxt = this.tmpTxt.replace('____','')
                    this.tmpTxt += JSON.parse(e.data.replace(/'/g, '"')).msg
                    this.id = JSON.parse(e.data.replace(/'/g, '"')).id
                    waitFlag = true
                  }, false);
  
                  // 自定义 EventHandler，在收到 event 字段为 slide 的消息时触发
                  source.addEventListener('slide', e => {
                    console.log(`data: ${e.data}`); // => data: 7
                  }, false);
  
                  // 连接异常时会触发 error 事件并自动重连
                  source.addEventListener('error', e => {
                    if (e.target.readyState === EventSource.CLOSED) {
                      console.log('Disconnected');
                      waitFlag = false
                      clearInterval(ain)
                      this.tmpTxt = this.tmpTxt.replace('______','')
                      this.tmpTxt = this.tmpTxt.replace('____','')
                      this.canSend=true
                      this.tmpTxt=''
                      this.msg.push({role:'assistant',content:this.tmpTxt})
                      this.messages.push({role:'assistant',content:this.tmpTxt})
                      alert('Disconnected')
                    } else if (e.target.readyState === EventSource.CONNECTING) {
                      console.log('Connecting...');
                    }
                  }, false);
                } else {
                  console.error('Your browser doesn\'t support SSE');
                  alsert('Your browser doesn\'t support SSE');
                }
              }
            })
            .catch(error => {
              waitFlag = false
              clearInterval(ain)
              this.tmpTxt = this.tmpTxt.replace('______','')
              this.tmpTxt = this.tmpTxt.replace('____','')
              this.canSend=true
              this.tmpTxt=error
              this.msg.push({role:'assistant',content:this.tmpTxt})
              //滚动到底部
              chatBody.scrollTop = chatBody.scrollHeight
              console.log(error);
            });


        // //处理流数据
        // let decoder = new TextDecoder()
        // let parseStream=(value)=>{
        //   let tmp=decoder.decode(value)
        //   let data=tmp.split('\n')
        //   for (let v of data) {
        //     if(!v)continue
        //     if(v=='data: [DONE]')break
        //     let json=JSON.parse(v.substr(6))
        //     let text=json.choices[0].delta.content??''
        //     if(text=='\n\n'&&this.tmpTxt=='')continue//去掉开头的换行
        //     if(text)this.tmpTxt+=text
        //   }
        // }

      //   //结束处理
      //   let fin=()=>{
      //     this.msg.push({role:'assistant',content:this.tmpTxt})
      //     this.canSend=true
      //     this.tmpTxt=''

      //     //消息长度控制在30
      //     let spli=this.msg.length-30
      //     if(spli>0)this.msg.splice(0,spli)

      //     //滚动到底部
      //     chatBody.scrollTop = chatBody.scrollHeight
      //   }

      //   fetch('https://api.liaocatcat.xyz/v1/chat/completions',{
      //     method:'POST',
      //     headers:{
      //       'Content-Type':'application/json',
      //       'Authorization': 'Bearer '+this.apikey
      //     },
      //     body:JSON.stringify({
      //       model: 'gpt-3.5-turbo',
      //       messages:this.msg,
      //       temperature: 0.6,
      //       stream: true,
      //     }),
      //   }).then(o=>{
      //     let reader = o.body.getReader()
      //     new ReadableStream({
      //       start(controller) {
      //         let push=()=>{
      //           reader.read().then(({ done, value }) => {
      //             if (done) {
      //               fin()//结束处理
      //               controller.close()
      //               return
      //             }

      //             parseStream(value)//处理数据

      //             controller.enqueue(value)
      //             push()
      //           })
      //         }
      //         push()
      //       },
      //     })
      //   }).catch(e=>{
      //     this.$alert(e.message)
      //   })
      }
      //保存密钥
      // saveKey(){
      //   localStorage.setItem(this.saveName, this.apikey)
      //   this.$message.success('已保存')
      // },
      //删除密钥
      // delKey(){
      //   localStorage.removeItem(this.saveName)
      //   this.apikey=''
      //   this.$message.success('成功删除缓存密钥')
      // }
    }
  })
  app.use(ElementPlus)
  app.mount("#app")
</script>
</body>
</html>